package cn.com.libertymutual.core.web.util;

import static cn.com.libertymutual.core.util.Constants.POST;
import static cn.com.libertymutual.core.util.Constants.UTF8;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.URLDecoder;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.StringTokenizer;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.util.UrlPathHelper;

import com.google.common.base.Joiner;
import com.google.common.base.Strings;

import cn.com.libertymutual.core.security.encoder.Md5PwdEncoder;
import cn.com.libertymutual.core.security.encoder.PwdEncoder;
import cn.com.libertymutual.core.util.Constants;

/**
 * HttpServletRequest帮助类
 * 
 * @author liufang
 * 
 */
public class RequestUtils {
	private static final Logger log = LoggerFactory.getLogger(RequestUtils.class);

	/**
	 * 获取QueryString的参数，并使用URLDecoder以UTF-8格式转码。如果请求是以post方法提交的，
	 * 那么将通过HttpServletRequest#getParameter获取。
	 * 
	 * @param request
	 *            web请求
	 * @param name
	 *            参数名称
	 * @return
	 * @throws Exception 
	 */
	public static String getQueryParam(HttpServletRequest request, String name) throws Exception {

		if (Strings.isNullOrEmpty(name)) {
			return null;
		}
		if (request.getMethod().equalsIgnoreCase(POST)) {
			return request.getParameter(name);
		}
		String s = request.getQueryString();
		if (Strings.isNullOrEmpty(s)) {
			return null;
		}
		s = URLDecoder.decode(s, UTF8);
		String[] values = parseQueryString(s).get(name);
		if (values != null && values.length > 0) {
			return values[values.length - 1];
		}
		return null;
	}

	/**
	 * 获得本机实际IP
	 * @return
	 * @throws Exception
	 */
	public static String[] getLocalHostLANAddress() {
		String[] ipInfos = new String[2];

		try {
			InetAddress candidateAddress = null;
			// 遍历所有的网络接口
			for (Enumeration ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements();) {
				NetworkInterface iface = (NetworkInterface) ifaces.nextElement();
				// 在所有的接口下再遍历IP
				for (Enumeration inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements();) {
					InetAddress inetAddr = (InetAddress) inetAddrs.nextElement();
					if (!inetAddr.isLoopbackAddress()) {// 排除loopback类型地址
						if (inetAddr.isSiteLocalAddress()) {
							// 如果是site-local地址，就是它了
							ipInfos[0] = inetAddr.getHostAddress();
							ipInfos[1] = inetAddr.getHostName();
							return ipInfos;
						} else if (candidateAddress == null) {
							// site-local类型的地址未被发现，先记录候选地址
							candidateAddress = inetAddr;
						}
					}
				}
			}
			if (candidateAddress != null) {
				ipInfos[0] = candidateAddress.getHostAddress();
				ipInfos[1] = candidateAddress.getHostName();
				return ipInfos;
			}
			// 如果没有发现 non-loopback地址.只能用最次选的方案
			InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
			ipInfos[0] = jdkSuppliedAddress.getHostAddress();
			ipInfos[1] = jdkSuppliedAddress.getHostName();
		} catch (Exception e) {
			e.printStackTrace();
			ipInfos[0] = "127.0.0.1";
			ipInfos[1] = "localhost";
		}
		return ipInfos;
	}

	/**
	 * 获得本机IP与主机名
	 * @return String[]  [0]-IP地址、[1]=主机名
	 */
	@Deprecated
	public static String[] getGetIPAddressAndHostnameFromLocalMachine() {
		String[] ipInfos = new String[2];

		try {

			InetAddress inetAddr = InetAddress.getLocalHost();
			byte[] addr = inetAddr.getAddress();

			// Convert to dot representation
			String ipAddr = "";
			for (int i = 0; i < addr.length; i++) {
				if (i > 0) {
					ipAddr += ".";
				}
				ipAddr += addr[i] & 0xFF;
			}

			String hostname = inetAddr.getHostName();

			log.info("IP Address: " + ipAddr);
			log.info("Hostname: " + hostname);
			ipInfos[0] = ipAddr;
			ipInfos[1] = hostname;
		} catch (UnknownHostException e) {
			log.error("Host not found: " + e.getMessage());
		}

		return ipInfos;
	}

	public static Map<String, Object> getQueryParams(HttpServletRequest request) throws Exception {
		Map<String, Object> params = null;

		Map<String, String[]> map;
		if (request.getMethod().equalsIgnoreCase(POST)) {
			map = request.getParameterMap();
		} else {
			String s = request.getQueryString();
			if (Strings.isNullOrEmpty(s)) {
				return new HashMap<String, Object>();
			}

			s = URLDecoder.decode(s, UTF8);
			map = parseQueryString(s);
		}

		params = new HashMap<String, Object>(map.size());
		int len;
		for (Map.Entry<String, String[]> entry : map.entrySet()) {
			len = entry.getValue().length;
			if (len == 1) {
				params.put(entry.getKey(), entry.getValue()[0]);
			} else if (len > 1) {
				params.put(entry.getKey(), entry.getValue());
			}
		}

		return params;
	}

	/**
	 * 
	 * Parses a query string passed from the client to the server and builds a
	 * <code>HashTable</code> object with key-value pairs. The query string
	 * should be in the form of a string packaged by the GET or POST method,
	 * that is, it should have key-value pairs in the form <i>key=value</i>,
	 * with each pair separated from the next by a &amp; character.
	 * 
	 * <p>
	 * A key can appear more than once in the query string with different
	 * values. However, the key appears only once in the hashtable, with its
	 * value being an array of strings containing the multiple values sent by
	 * the query string.
	 * 
	 * <p>
	 * The keys and values in the hashtable are stored in their decoded form, so
	 * any + characters are converted to spaces, and characters sent in
	 * hexadecimal notation (like <i>%xx</i>) are converted to ASCII characters.
	 * 
	 * @param s
	 *            a string containing the query to be parsed
	 * 
	 * @return a <code>HashTable</code> object built from the parsed key-value
	 *         pairs
	 * 
	 * @exception IllegalArgumentException
	 *                if the query string is invalid
	 * 
	 */
	public static Map<String, String[]> parseQueryString(String s) {
		Map<String, String[]> ht = null;
		String valArray[] = null;
		if (s == null) {
			throw new IllegalArgumentException();
		}
		ht = new HashMap<String, String[]>();
		StringTokenizer st = new StringTokenizer(s, "&");
		while (st.hasMoreTokens()) {
			String pair = (String) st.nextToken();
			int pos = pair.indexOf('=');
			if (pos == -1) {
				continue;
			}
			String key = pair.substring(0, pos);
			String val = pair.substring(pos + 1, pair.length());
			if (ht.containsKey(key)) {
				String oldVals[] = (String[]) ht.get(key);
				valArray = new String[oldVals.length + 1];
				for (int i = 0; i < oldVals.length; i++) {
					valArray[i] = oldVals[i];
				}
				valArray[oldVals.length] = val;
			} else {
				valArray = new String[1];
				valArray[0] = val;
			}
			ht.put(key, valArray);
		}

		return ht;
	}

	public static Map<String, String> getRequestMap(HttpServletRequest request, String prefix) {
		Map<String, String> map = null;

		map = new HashMap<String, String>();
		Enumeration<String> names = request.getParameterNames();
		String name;
		while (names.hasMoreElements()) {
			name = names.nextElement();
			if (name.startsWith(prefix)) {
				request.getParameterValues(name);
				/// map.put(name.substring(prefix.length()),
				/// StringUtils.join(request.getParameterValues(name), ','));
				map.put(name.substring(prefix.length()), Joiner.on(',').join(request.getParameterValues(name)));

			}
		}
		return map;
	}

	/**
	 * 获取访问者IP
	 * 
	 * 在一般情况下使用Request.getRemoteAddr()即可，但是经过nginx等反向代理软件后，这个方法会失效。
	 * 
	 * 本方法先从Header中获取X-Real-IP，如果不存在再从X-Forwarded-For获得第一个IP(用,分割)，
	 * 如果还不存在则调用Request .getRemoteAddr()。
	 * 
	 * @param request
	 * @return
	 */
	public static String getIpAddr2(HttpServletRequest request) {
		String ip = request.getHeader("X-Real-IP");
		if (!Strings.isNullOrEmpty(ip) && !"unknown".equalsIgnoreCase(ip)) {
			return ip;
		}
		ip = request.getHeader("X-Forwarded-For");
		if (!Strings.isNullOrEmpty(ip) && !"unknown".equalsIgnoreCase(ip)) {
			// 多次反向代理后会有多个IP值，第一个为真实IP。
			String ips[] = ip.split("\\,");
			return ips[0];
			/*
			 * int index = ip.indexOf(','); if (index != -1) { return ip.substring(0,
			 * index); } else { return ip; }
			 */
		}
		return request.getRemoteAddr();
	}

	public static String getIpAddress(HttpServletRequest request) {

		String ip = request.getHeader("x-forwarded-for");

		String localIP = "127.0.0.1";

		if ((ip == null) || (ip.length() == 0) || (ip.equalsIgnoreCase(localIP)) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if ((ip == null) || (ip.length() == 0) || (ip.equalsIgnoreCase(localIP)) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if ((ip == null) || (ip.length() == 0) || (ip.equalsIgnoreCase(localIP)) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}

	public static String getIpAddr(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");
		if (!Strings.isNullOrEmpty(ip) && !"unknown".equalsIgnoreCase(ip)) {
			// 多次反向代理后会有多个IP值，第一个为真实IP。
			String ips[] = ip.split("\\,");
			return ips[0];
		}

		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("X-Real-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}

		return ip;
	}

	/**
	 * 获得当的访问路径
	 * 
	 * HttpServletRequest.getRequestURL+"?"+HttpServletRequest.getQueryString
	 * 
	 * @param request
	 * @return
	 */
	public static String getLocation(HttpServletRequest request) {
		StringBuffer buff = null;
		UrlPathHelper helper = new UrlPathHelper();
		buff = request.getRequestURL();
		String uri = request.getRequestURI();
		String origUri = helper.getOriginatingRequestUri(request);
		buff.replace(buff.length() - uri.length(), buff.length(), origUri);
		String queryString = helper.getOriginatingQueryString(request);
		if (queryString != null) {
			buff.append("?").append(queryString);
		}
		return buff.toString();
	}

	/**
	 * 获得请求的session id，但是HttpServletRequest#getRequestedSessionId()方法有一些问题。
	 * 当存在部署路径的时候，会获取到根路径下的jsessionid。
	 * 
	 * @see HttpServletRequest#getRequestedSessionId()
	 * 
	 * @param request
	 * @return
	 */
	public static String getRequestedSessionId(HttpServletRequest request) {
		String sid = request.getRequestedSessionId();
		String ctx = request.getContextPath();
		// 如果session id是从url中获取，或者部署路径为空，那么是在正确的。
		if (request.isRequestedSessionIdFromURL() || Strings.isNullOrEmpty(ctx)) {
			return sid;
		} else {
			// 手动从cookie获取
			Cookie cookie = CookieUtils.getCookie(request, Constants.JSESSION_COOKIE);
			if (cookie != null) {
				return cookie.getValue();
			}
		}
		return null;
	}

	/**
	 * 取请求文件的路径，本来可以request.getServletPath(),但是weblogic有bug不支持此函数
	 * 
	 * @param request
	 * @return
	 */
	public static String getPath(HttpServletRequest request) {
		String path = request.getPathInfo();
		if (path == null)
			return request.getServletPath();
		return path;
	}

	public static boolean isAjax(HttpServletRequest request) {

		// HttpServletRequest request =
		// ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();

		/*
		 * Enumeration<String> em = request.getHeaderNames(); while(
		 * em.hasMoreElements() ) { String key = em.nextElement(); System.out.println(
		 * key+"="+request.getHeader(key) ); }
		 */

		// request.getHeader("x-requested-with")

		/// System.out.println( request.getHeader("x-requested-with") );

		if (request.getHeader("x-requested-with") != null && request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest")) {
			return true;
		}

		return false;
	}

	public static boolean isAjax() {

		ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		if( servletRequestAttributes == null ) {
			return false;
		}
		
		HttpServletRequest request = servletRequestAttributes.getRequest();

		return isAjax(request);
	}

	public static boolean isRestRequest(HttpServletRequest request) {

		String contentType = request.getContentType();

		return isAjax(request) || (!Strings.isNullOrEmpty(contentType) && contentType.indexOf("application/") >= 0);
	}

	public static String getHeaderTokenId() {

		return getHeader("insure-tk");

	}

	public static String getHeader(String name) {
		ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		if( servletRequestAttributes == null ) {
			return "";
		}
		
		HttpServletRequest request = servletRequestAttributes.getRequest();

		return request.getHeader(name);

	}
	/**
	 * 创建MQ平台所需要的HttpHeader
	 * @param username
	 * @param password
	 * @return
	 */
	public static HttpHeaders genMQEncryptHttpHeaders(String username, String password) {
		PwdEncoder pwdEncoder = new Md5PwdEncoder();
		long timeline = new Date().getTime();
		HttpHeaders headers = new HttpHeaders();
		headers.add("partyId", username);
		headers.add("_time", String.valueOf(timeline));
		headers.add("Authorization", pwdEncoder.encodePassword(String.format("%s-%s", timeline, username ), password));
		return headers;
	}

	/**
	 * 创建三方平台所需要的HttpHeader
	 * @param username
	 * @param password
	 * @return
	 */
	public static HttpHeaders genEncryptHttpHeaders(String username, String password) {
		return genEncryptHttpHeaders(username, password, null);
	}

	/**
	 * 创建三方平台所需要的HttpHeader	
	 * @param username
	 * @param password
	 * @param mediaType
	 * @return
	 */
	public static HttpHeaders genEncryptHttpHeaders(String username, String password, MediaType mediaType) {
		return genEncryptHttpHeaders(username, password, mediaType, null);
	}

	public static HttpHeaders genEncryptHttpHeaders(String username, String password, MediaType mediaType, Map<String, String> headerMap) {
		PwdEncoder pwdEncoder = new Md5PwdEncoder();

		long timeline = new Date().getTime();
		HttpHeaders headers = new HttpHeaders();
		headers.add("account-code", username);
		headers.add("request-time", String.valueOf(timeline));
		headers.add("soa-token", pwdEncoder.encodePassword(password, String.valueOf(timeline)));

//		headers.add("account-code", username);
//		headers.add("request-time", String.valueOf(timeline));
//		headers.add("soa-token", pwdEncoder.encodePassword(password, String.valueOf(timeline)));
//		
		
		if (null != mediaType) {
			headers.setContentType(mediaType);
		}

		if (headerMap != null && headerMap.size() > 0) {
			Set<Entry<String, String>> headerSet = headerMap.entrySet();
			for (Entry<String, String> entry : headerSet) {
				headers.add(entry.getKey(), entry.getValue());
			}
		}

		return headers;
	}
	public static HttpHeaders genMQHttpHeaders(String username, String password) {
		PwdEncoder pwdEncoder = new Md5PwdEncoder();

		long timeline = new Date().getTime();
		HttpHeaders headers = new HttpHeaders();
		headers.add("partyId", username);
		headers.add("Authorization", pwdEncoder.encodePassword(String.format("%s-%s", timeline, username ), password));
		headers.add("_time", String.valueOf(timeline));
		
//		headers.add("soa-token", pwdEncoder.encodePassword(password, String.valueOf(timeline)));

//		headers.add("account-code", username);
//		headers.add("request-time", String.valueOf(timeline));
//		headers.add("soa-token", pwdEncoder.encodePassword(password, String.valueOf(timeline)));
//		
		
		/*if (null != mediaType) {
			headers.setContentType(mediaType);
		}

		if (headerMap != null && headerMap.size() > 0) {
			Set<Entry<String, String>> headerSet = headerMap.entrySet();
			for (Entry<String, String> entry : headerSet) {
				headers.add(entry.getKey(), entry.getValue());
			}
		}*/

		return headers;
	}
}
